/*********************************************************************
 * File Name: chrdevbase.c
 * Summary: Basic character device driver example
 * Description: 
 * This file implements a basic character device driver for Linux kernel
 * development learning and demonstration purposes.
 *
 * Key Features:
 * - Character device registration and management
 * - User space to kernel space data transfer
 * - Basic file operations (open, read, write, close)
 * - Error handling and parameter validation
 *
 * Modification History:
 * Version    Author      Date        Changes
 *--------------------------------------------------
 * 1.00.00    yushuang    2025-09-11  Initial release
 *--------------------------------------------------
 ********************************************************************/

#include <linux/types.h>   /**< ssize_t, size_t, loff_t, __user */
#include <linux/kernel.h>  /**< printk, memcpy, error codes */
#include <linux/fs.h>      /**< file_operations, register_chrdev, copy_to_user */
#include <linux/init.h>    /**< __init, __exit, module_init, module_exit */
#include <linux/module.h>  /**< THIS_MODULE, MODULE_LICENSE, MODULE_AUTHOR */
#include <linux/ide.h>

#define CHRDEVBASE_MAJOR	200				/**< Major device number */
#define CHRDEVBASE_NAME		"chrdevbase" 	/**< Device name */

static char readbuf[100];		/**< Read buffer */
static char writebuf[100];		/**< Write buffer */
static char kerneldata[] = {"kernel data!"};	/**< Kernel data to send */

/**
 * @brief Open device
 * @param inode Device inode
 * @param filp File pointer
 * @return 0 on success, negative on failure
 */
static int chrdevbase_open(struct inode *inode, struct file *filp)
{
	(void)inode;  /* Suppress unused parameter warning */
	(void)filp;   /* Suppress unused parameter warning */
	return 0;
}

/**
 * @brief Read data from device
 * @param filp File pointer
 * @param buf User space buffer
 * @param cnt Number of bytes to read
 * @param offt File offset
 * @return Number of bytes read, negative on failure
 */
static ssize_t chrdevbase_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
	int retvalue = 0;
	int data_len = sizeof(kerneldata);
	int copy_len;
	
	/* Parameter validation */
	if (!buf || cnt == 0)
	{
		return -EINVAL;
	}
	
	/* Calculate actual copy length */
	copy_len = (cnt < data_len) ? cnt : data_len;
	
	/* Send data to user space */
	memcpy(readbuf, kerneldata, data_len);
	retvalue = copy_to_user(buf, readbuf, copy_len);
	if (retvalue == 0)
	{
		printk("chrdevbase: read operation successful, sent %d bytes\r\n", copy_len);
		return copy_len;
	}
	else
	{
		printk("chrdevbase: read operation failed, copy_to_user error\r\n");
		return -EFAULT;
	}
}

/**
 * @brief Write data to device
 * @param filp File pointer
 * @param buf User space buffer
 * @param cnt Number of bytes to write
 * @param offt File offset
 * @return Number of bytes written, negative on failure
 */
static ssize_t chrdevbase_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	int retvalue = 0;
	int max_len = sizeof(writebuf) - 1;
	int copy_len;
	
	/* Parameter validation */
	if (!buf || cnt == 0)
	{
		return -EINVAL;
	}
	
	/* Calculate copy length to prevent buffer overflow */
	copy_len = (cnt < max_len) ? cnt : max_len;
	
	/* Receive data from user space */
	retvalue = copy_from_user(writebuf, buf, copy_len);
	if (retvalue == 0)
	{
		writebuf[copy_len] = '\0';  /* Ensure string termination */
		printk("chrdevbase: write operation successful, received %d bytes: '%s'\r\n", copy_len, writebuf);
		return copy_len;
	}
	else
	{
		printk("chrdevbase: write operation failed, copy_from_user error\r\n");
		return -EFAULT;
	}
}

/**
 * @brief Close/release device
 * @param inode Device inode
 * @param filp File pointer
 * @return 0 on success, negative on failure
 */
static int chrdevbase_release(struct inode *inode, struct file *filp)
{
	(void)inode;  /* Suppress unused parameter warning */
	(void)filp;   /* Suppress unused parameter warning */
	return 0;
}

/** Device file operations structure */
static struct file_operations chrdevbase_fops = 
{
	.owner = THIS_MODULE,	
	.open = chrdevbase_open,
	.read = chrdevbase_read,
	.write = chrdevbase_write,
	.release = chrdevbase_release,
};

/**
 * @brief Driver initialization function
 * @return 0 on success, negative on failure
 */
static int __init chrdevbase_init(void)
{
	int retvalue = 0;

	/* Register character device driver */
	retvalue = register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME, &chrdevbase_fops);
	if (retvalue < 0)
	{
		printk("chrdevbase driver register failed, error: %d\r\n", retvalue);
		return retvalue;
	}
	printk("chrdevbase init! major: %d\r\n", CHRDEVBASE_MAJOR);
	return 0;
}

/**
 * @brief Driver cleanup function
 */
static void __exit chrdevbase_exit(void)
{
	/* Unregister character device driver */
	unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME);
}

/* Register init and exit functions */
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);

/* Module license and author information */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("yushuang");
